ws-server.js ➔ sendJSON   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
c 1
b 0
f 0
nc 1
dl 0
loc 3
ccs 1
cts 1
cp 1
crap 1
rs 10
nop 2
1
/**
2
 * Web Sockets server.
3
 *
4
 * @module  src/ws-server
5
 */
6
7
"use strict";
8
9 1
const WebSocket = require("ws");
10
11
12
/**
13
 * Server object prototype.
14
 */
15 1
const wsServerProto = {
16
    /**
17
     * Underlying Web Sockets server instance.
18
     *
19
     * @type    {WebSocket.Server}
20
     */
21
    server: null,
22
    
23
    
24
    /**
25
     * Broadcasts a message to all connected clients.
26
     *
27
     * @param   {any}       data        Message to send.
28
     * @param   {WebSocket} [exclude]   WebSocket instance to exclude from broadcast (if any).
29
     */
30
    broadcast: function broadcast(data, exclude) {
31 4
        this.server.clients.forEach(function(client) {
32 8
            if (exclude === client || client.readyState !== WebSocket.OPEN) {
33 2
                return;
34
            }
35 6
            client.send(data);
36
        });
37
    },
38
    
39
    
40
    /**
41
     * Broadcasts an object as a JSON string to all connected clients.
42
     *
43
     * @param   {object}    data        Object to send.
44
     * @param   {WebSocket} [exclude]   WebSocket instance to exclude from broadcast (if any).
45
     */
46
    broadcastJSON: function broadcastJSON(data, exclude) {
47 2
        this.broadcast(JSON.stringify(data), exclude);
48
    },
49
    
50
    
51
    /**
52
     * Sends an object as a JSON string to a specific client.
53
     *
54
     * @param   {WebSocket} socket  WebSocket instance to send to.
55
     * @param   {object}    data    Object to send.
56
     */
57
    sendJSON: function sendJSON(socket, data) {
58 1
        socket.send(JSON.stringify(data));
59
    }
60
};
61
62
63
/**
64
 * Handles an incoming connection.
65
 *
66
 * @param   {WebSocket}             socket  WebSocket instance.
67
 * @param   {http.IncomingMessage}  req     Request object.
68
 * @param   {object}                config  Server instance configuration object.
69
 */
70
function handleConnection(socket, req, config) {
71
    // set up client object
72 6
    let client = {
73
        socket: socket,
74
        request: req
75
    };
76
    
77
    // set up message handler, if any
78 6
    if (typeof config.messageHandler == "function") {
79 1
        socket.on("message", function(data) {
80 1
            config.messageHandler(data, client);
81
        });
82
    }
83
    
84
    // set up error handler, if any
85 6
    if (typeof config.errorHandler == "function") {
86 1
        socket.on("error", function(err) {
87 1
            config.errorHandler(err, client);
88
        });
89
    }
90
    
91
    // set up disconnection handler, if any
92 6
    if (typeof config.closeHandler == "function") {
93 2
        socket.on("close", function(code, reason) {
94 2
            config.closeHandler(code, reason, client);
95
        });
96
    }
97
    
98
    // set up ping cycle, if requested
99 6
    if (config.timeout) {
100 1
        socket.pingPending = false;
101 1
        socket.on("pong", function() {
102 1
            socket.pingPending = false;
103
        });
104
    }
105
    
106
    // call connection handler, if any
107 6
    if (typeof config.connectionHandler == "function") {
108 4
        config.connectionHandler(client);
109
    }
110
}
111
112
113
/**
114
 * Pings a connected client, disconnecting it in case of no response from last ping.
115
 *
116
 * @param   {WebSocket}     socket  WebSocket instance.
117
 */
118
function ping(socket) {
119
    // forcibly close connection if no reply since last ping
120 3
    if (socket.pingPending) {
121 1
        socket.terminate();
122 1
        return;
123
    }
124
    
125
    // send next ping
126 2
    socket.pingPending = true;
127 2
    socket.ping("", false, true);
128
}
129
130
131
/**
132
 * Creates a new server instance.
133
 *
134
 * @param   {object}    serverOptions               Construction options for the underlying Web 
135
 *                                                  Sockets server. Note that the clientTracking
136
 *                                                  and handleProtocols properties are overwritten.
137
 * @param   {object}    [config]                    Configuration object:
138
 * @param   {number}    [config.timeout]              Ping timeout in milliseconds.
139
 * @param   {function}  [config.protocolHandler]      Protocol selection handler.
140
 * @param   {function}  [config.connectionHandler]    Connection handler.
141
 * @param   {function}  [config.messageHandler]       Message reception handler.
142
 * @param   {function}  [config.errorHandler]         Error handler.
143
 * @param   {function}  [config.closeHandler]         Disconnection handler.
144
 */
145
function createServer(serverOptions, config) {
146 7
    config = config || {};
147
    
148
    // set up protocol handler, if any
149 7
    if (typeof config.protocolHandler == "function") {
150 1
        serverOptions.handleProtocols = config.protocolHandler;
151
    }
152
    
153
    // set up Web Sockets server
154 7
    serverOptions.clientTracking = true;
155 7
    let server = new WebSocket.Server(serverOptions);
156 7
    server.on("connection", function(socket, req) {
157 6
        handleConnection(socket, req, config);
158
    });
159
    
160
    // start ping cycle, if requested
161 7
    if (config.timeout) {
162 1
        setInterval(function() {
163 3
            server.clients.forEach(ping);
164
        }, config.timeout);
165
    }
166
    
167
    // create and return server instance
168 7
    let wsServer = Object.create(wsServerProto);
169 7
    wsServer.server = server;
170 7
    return wsServer;
171
}
172
173
174
module.exports = createServer;
175